PowerTCP Mail for .NET
Working with MailMessage



This topic provides background for creating and examining messages using the MailMessage object.

Creating a Basic Message - Headers, Text, Attachments

There are three ways to create a MailMessage object in Mail for .NET:

  1. From a Stream or file containing an encoded message
  2. From an existing System.Net.Mail.MailMessage object
  3. From scratch (by composing it in code)

The first two methods involve decoding existing messages from some other format. This topic will focus on the third method - creating a message from scratch.

Most mail messages consist of three major components: the headers, the text message, and attachments. This section examines these components.

Headers

The header fields (headers) contain important information related to the content of the message. The MailMessage object contains properties for required and commonly used header fields.

Recipients: There are three properties to set recipients - To, Cc and Bcc. Set the appropriate property to specify a recipient. To is for recipients directly affected by the message. Cc (Carbon Copy) is for recipients who should see the message but are not directly affected. Bcc (Blind Carbon Copy) is for recipients who should be hidden from other recipients (these recipients will receive the message, but other recipients will not know they have).

Sender: Set the From property to specify the sender of the message.

Subject: Set the Subject property to specify the message subject. Typically this is a summary of the content.

The MailMessage also has properties for Priority, ReplyTo and Date, but it is generally not required or necessary to change their default values.

Text Message

Set MailMessage.Text to add unformatted text to the message. The "HTML Messages" section below discusses HTML text.

Attachments

Attachments are included files that may be saved to disk when a message is received. To add an attachment to a message, create an Attachment object using a local file path, and add it to the MailMessage.Attachments collection.

Example Code

This example creates a simple text message with one attachment.

C#
Copy Code
private MailMessage CreateMailMessageWithAttachment()
{
    MailMessage message = new MailMessage();
    message.To = "to@dart.com";
    message.From = "from@dart.com";
    message.Subject = "This is a simple message";
    message.Text = "Please see the attached file.";
    message.Attachments.Add(new Attachment("C:\\File\\myAttachment.jpg"));
    return message;
}
Visual Basic
Copy Code
Private Function CreateMailMessageWithAttachment() As MailMessage
    Dim message As New MailMessage()
    message.To = "to@dart.com"
    message.From = "from@dart.com"
    message.Subject = "This is a simple message"
    message.Text = "Please see the attached file."
    message.Attachments.Add(New Attachment("C:\File\myAttachment.jpg"))
    Return message
End Function

Advanced Topics - Custom Headers, Parts Collection

Custom Headers

The Basics section above demonstrates how to add standard headers to a message using properties of the MailMessage object. MailMessage also includes a Headers property which can be used to add custom header lines to the message. Note that all message Parts include the Headers property (and can therefore have customized headers), not just the MailMessage object.

MultipartContent Collection

MailMessage.Parts holds all Parts in the MailMessage, including Textparts (which have string content), Attachments and Resources (which have FileInfo or byte[] content), and Multiparts (which have their own Multipart.Parts collection). The hierarchical structure of a MailMessage is exposed by nested Multipart.Parts collections.

Note that a MailMessage or Multipart with a single Part will encode itself by removing the Multipart "level". In other words, MailMessage will never generate an encoded message containing a Multipart with only one child Part.

Example Code

This example demonstrates custom Headers and the Multipart.Parts collection. Custom header fields are added to the MailMessage and its Parts. Textparts are added to a Multipart, which is in turn added to the MailMessage. A Resource part is also added to the MailMessage.

C#
Copy Code
private MailMessage addCustomParts(MailMessage message)
{
    //Add custom header field to message
    message.Headers.Add("X-Alt-Message", new HeaderField("X-Alt-Message", "Message with Alternatives"));

    //Create and add alternative parts to a multipart part
    Textpart part1 = new Textpart("This is alternative 1.");
    part1.Headers.Add("X-Alt", new HeaderField("X-Alt", "Alternative 1"));

    Textpart part2 = new Textpart("This is alternative 2.");
    part2.Headers.Add("X-Alt", new HeaderField("X-Alt", "Alternative 2"));

    Multipart multipart = new Multipart(Multipart.Alternative);
    multipart.Parts.Add(part1);
    multipart.Parts.Add(part2);

    //Add a MultiPart to the MailMessage
    message.Parts.Add(multipart);

    //Add a Resource part to the MailMessage
    Resource resource = new Resource(new FileStream(Application.StartupPath + "\\myImage.jpg", FileMode.Open), "myImage.jpg");
    resource.ContentType = new ContentType("image/jpeg");
    resource.ContentId = "<myImage>";
    message.Parts.Add(resource);
    return message;
}
Visual Basic
Copy Code
Private Function addCustomParts(ByVal message As MailMessage) As MailMessage
    'Add custom header field to message
    message.Headers.Add("X-Alt-Message", New HeaderField("X-Alt-Message", "Message with Alternatives"))

    'Create and add alternative parts to a multipart part
    Dim part1 As New Textpart("This is alternative 1.")
    part1.Headers.Add("X-Alt", New HeaderField("X-Alt", "Alternative 1"))

    Dim part2 As New Textpart("This is alternative 2.")
    part2.Headers.Add("X-Alt", New HeaderField("X-Alt", "Alternative 2"))

    Dim multipart As New Multipart(Dart.Mail.Multipart.Alternative)
    multipart.Parts.Add(part1)
    multipart.Parts.Add(part2)

    'Add a MultiPart to the MailMessage
    message.Parts.Add(multipart)

    'Add a Resource part to the MailMessage
    Dim resource As New Resource(New FileStream(Application.StartupPath & "\myImage.jpg", FileMode.Open), "myImage.jpg")
    resource.ContentType = New ContentType("image/jpeg")
    resource.ContentId = "<myImage>"
    message.Parts.Add(resource)
    Return message
End Function

HTML Messages

HTML is used in messages that benefit from formatted text and images. This method adds a multipart/related part to an existing multipart/alternative message.

C#
Copy Code
private MailMessage addHtml(MailMessage message)
{
    string html = "This is my HTML message. It contains a couple of images." +
        "<img alt='image1.jpg' src='image1.jpg' />" +
        "<img alt='image2.jpg' src='image2.jpg' />";

    message.Parts.Add(new Multipart(null, html, new DirectoryInfo("C:\\Html Resources\\images").GetFiles()));
    return message;
}
Visual Basic
Copy Code
Private Function addHtml(ByVal message As MailMessage) As MailMessage
    Dim html As String = "This is my HTML message. It contains a couple of images." & "<img alt='image1.jpg' src='image1.jpg' />" & "<img alt='image2.jpg' src='image2.jpg' />"

    message.Parts.Add(New Multipart(Nothing, html, New DirectoryInfo("C:\Html Resources\images").GetFiles()))
    Return message
End Function

Encoding and Decoding

Sending or Saving a Message

PowerTCP Mail for .NET features "on-the-fly" encoding/decoding for a minimal memory/disk footprint. Encoding occurs when the Smtp component Sends a MailMessage to a mail server, when Save stores the message to disk or a Stream, and when ToString transforms the message to an encoded string.

Accessing Parts

All messages (including non-MIME messages) decode to MailMessage objects with a MIME structure. A MIME message contains parts which may hold data or parts of their own. The Parts property uses Filter to access different types of parts, including AttachmentResourceMultipartTextpart and Htmlpart, with a recursive option.

All Parts contain a Headers property, which is a Dictionary indexed by HeaderField.Name (for example, "Content-Type"). Many header fields are accessible as properties on the Part itself. For example, Textpart includes ContentType and ContentTransferEncoding properties. This is true for MailMessage as well (To, From, etc. See "Creating a Basic Message" above).

Parts provide a Content property to access the part's contents. Textpart.Content is a String, whereas Attachment.Content is a FileInfo and Resource.Content is a byte[]. MailMessage and Multipart objects contain a Parts collection instead of a Content property.

Saving Attachments and Resources

When a message is decoded, attachments are saved to Attachment.Directory, or stored in memory when the static Attachment.DecodeToMemory is true. When Attachment.DecodeToMemory is false, Attachment.Content.MoveTo() can be used to move the attachment to a permanent location. Attachment.GetContentStream() may be used to access the stream containing the file data when Attachment.DecodeToMemory is true. MailMessage.Attachments returns a flat list of all attachments contained in the email.

Inline files (parts with a Content Disposition header of 'inline') are represented by the Resource class and are only decoded to memory. Resource.Content returns a byte array containing the file data.

Example Code

C#
Copy Code
private void SaveAttachments(MailMessage message, string saveDirectory)
{
    foreach (Attachment attachment in message.Attachments)
        attachment.Content.CopyTo(System.IO.Path.Combine(saveDirectory, attachment.FileName));
}
//If Attachment.DecodeToMemory was set to true in your application:
private void SaveAttachmentsFromMemory(MailMessage message, string saveDirectory)
{
    //Stream.CopyTo() used here is only available in .NET 4.0 and up
    foreach (Attachment attachment in message.Attachments)
        using (FileStream fs = new FileStream(Path.Combine(saveDirectory, attachment.FileName), FileMode.CreateNew, FileAccess.Write))
            attachment.GetContentStream().CopyTo(fs);
}
//Saves inline files (files intended for display within the HTML message body)
private void SaveInlineFiles(MailMessage message, string saveDirectory)
{
    foreach (Resource res in message.Resources)
        using (FileStream fs = new FileStream(Path.Combine(saveDirectory, res.ContentType.Name), FileMode.CreateNew, FileAccess.Write))
            fs.Write(res.Content, 0, res.Content.Length);
}
Visual Basic
Copy Code
Private Sub SaveAttachments(ByVal message As MailMessage, ByVal saveDirectory As String)
    For Each attachment As Attachment In message.Attachments
        attachment.Content.CopyTo(System.IO.Path.Combine(saveDirectory, attachment.FileName))
    Next attachment
End Sub
'If Attachment.DecodeToMemory was set to true in your application:
Private Sub SaveAttachmentsFromMemory(ByVal message As MailMessage, ByVal saveDirectory As String)
    'Stream.CopyTo() used here is only available in .NET 4.0 and up
    For Each attachment As Attachment In message.Attachments
        Using fs As New FileStream(Path.Combine(saveDirectory, attachment.FileName), FileMode.CreateNew, FileAccess.Write)
            attachment.GetContentStream().CopyTo(fs)
        End Using
    Next attachment
End Sub
'Saves inline files (files intended for display within the HTML message body)
Private Sub SaveInlineFiles(ByVal message As MailMessage, ByVal saveDirectory As String)
    For Each res As Resource In message.Resources
        Using fs As New FileStream(Path.Combine(saveDirectory, res.ContentType.Name), FileMode.CreateNew, FileAccess.Write)
            fs.Write(res.Content, 0, res.Content.Length)
        End Using
    Next res
End Sub

Cloning Messages

Use MailMessage.Clone to create an exact copy of a MailMessage. Use a MailMessage as a template to create new messages or to create "Reply" or "Forward" messages as demonstrated in the code below.

C#
Copy Code
private MailMessage createCustomReplyMessage(MailMessage originalMessage, string replyText, string fromAddress)
{
    //Clone the original message
    MailMessage replyMessage = originalMessage.Clone() as MailMessage;

    //Set the recipient address
    if (!String.IsNullOrWhiteSpace(replyMessage.ReplyTo))
        replyMessage.To = replyMessage.ReplyTo;
    else if (!String.IsNullOrWhiteSpace(replyMessage.From))
        replyMessage.To = replyMessage.From;

    //Set the sender's address
    replyMessage.From = fromAddress;

    //Indicate that this is a reply in the subject
    replyMessage.Subject = "Re: " + originalMessage.Subject;

    //Clear some properties of the message
    replyMessage.ReplyTo = "";
    replyMessage.Cc = "";
    replyMessage.Sender = "";

    //Set the message text to be the reply followed by some of the original
    //message's header fields and the original text
    string messageText = replyText + Environment.NewLine + Environment.NewLine;
    messageText += "From: " + originalMessage.From + Environment.NewLine;
    messageText += "Sent: " + originalMessage.Date.ToString() + Environment.NewLine;
    messageText += "To: " + originalMessage.To + Environment.NewLine;
    messageText += "Subject: " + originalMessage.Subject + Environment.NewLine + Environment.NewLine;

    //Set the plain text
    replyMessage.Text = messageText + originalMessage.Text;

    //Set the HTML text
    string html = originalMessage.Html;

    //Locate place in HTML string to insert reply text
    int textStartIndex = html.ToLower().IndexOf("<body");
    if (textStartIndex == -1) 
        textStartIndex = html.ToLower().IndexOf("<html");
    textStartIndex = html.IndexOf(">", textStartIndex) + 1;

    //Insert reply text into HTML string and set the Body.Html
    messageText = "<div style=\"color:black\">" + messageText.Replace("\r\n", "<br />") + "</div>";
    html = html.Insert(textStartIndex, messageText);
    replyMessage.Html = html;
    return replyMessage;
}
Visual Basic
Copy Code
Private Function createCustomReplyMessage(ByVal originalMessage As MailMessage, ByVal replyText As String, ByVal fromAddress As String) As MailMessage
    'Clone the original message
    Dim replyMessage As MailMessage = TryCast(originalMessage.Clone(), MailMessage)

    'Set the recipient address
    If Not String.IsNullOrWhiteSpace(replyMessage.ReplyTo) Then
        replyMessage.To = replyMessage.ReplyTo
    ElseIf Not String.IsNullOrWhiteSpace(replyMessage.From) Then
        replyMessage.To = replyMessage.From
    End If

    'Set the sender's address
    replyMessage.From = fromAddress

    'Indicate that this is a reply in the subject
    replyMessage.Subject = "Re: " & originalMessage.Subject

    'Clear some properties of the message
    replyMessage.ReplyTo = ""
    replyMessage.Cc = ""
    replyMessage.Sender = ""

    'Set the message text to be the reply followed by some of the original
    'message's header fields and the original text
    Dim messageText As String = replyText & Environment.NewLine & Environment.NewLine
    messageText &= "From: " & originalMessage.From & Environment.NewLine
    messageText &= "Sent: " & originalMessage.Date.ToString() & Environment.NewLine
    messageText &= "To: " & originalMessage.To & Environment.NewLine
    messageText &= "Subject: " & originalMessage.Subject & Environment.NewLine & Environment.NewLine

    'Set the plain text
    replyMessage.Text = messageText & originalMessage.Text

    'Set the HTML text
    Dim html As String = originalMessage.Html

    'Locate place in HTML string to insert reply text
    Dim textStartIndex As Integer = html.ToLower().IndexOf("<body")
    If textStartIndex = -1 Then
        textStartIndex = html.ToLower().IndexOf("<html")
    End If
    textStartIndex = html.IndexOf(">", textStartIndex) + 1

    'Insert reply text into HTML string and set the Body.Html
    messageText = "<div style=""color:black"">" & messageText.Replace(vbCrLf, "<br />") & "</div>"
    html = html.Insert(textStartIndex, messageText)
    replyMessage.Html = html
    Return replyMessage
End Function

S/MIME

The S/MIME Overview page provides a background on signing and encrypting messages using S/MIME. 

MailMessage provides three methods for encoding and decoding S/MIME messages. SecureSign signs a message using a certificate specifically issued for the sender's email address. SecureEncrypt encrypts a message using a certificate issued for the recipient's email address. SecureDecode decodes a signed and/or encrypted message. All three methods have a parameterless overload that automatically searches the appropriate certificate store for a matching email certificate. The code below demonstrates each method overload with parameters.

C#
Copy Code
private MailMessage getSignedMessage(MailMessage message)
{
    //Find the signing certificate in the "CurrentUser/My" certificate store
    //The following code results in the same signed message as "message.SecureSign();"
    X509Store myPersonalStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
    myPersonalStore.Open(OpenFlags.ReadOnly);
    foreach (X509Certificate2 certificate in myPersonalStore.Certificates)
    {
        if (certificate.Subject.Contains("E=" + message.From.ToString()))
        {
            //Sign the message
            message.SecureSign(certificate, X509IncludeOption.ExcludeRoot, DigestAlgorithm.Sha1, true, false);
            return message;
        }
    }
    return null;
}
C#
Copy Code
private MailMessage getEncryptedMessage(MailMessage message)
{
    //Find the encrypting certificate in the CurrentUser/AddressBook store
    //The following code results in the same encrypted message as "message.SecureEncrypt();"
    X509Certificate2Collection encryptingCertificates = new X509Certificate2Collection();
    X509Store addressBookStore = new X509Store(StoreName.AddressBook, StoreLocation.CurrentUser);
    addressBookStore.Open(OpenFlags.ReadOnly);
    foreach (X509Certificate2 certificate in addressBookStore.Certificates)
    {
        if (certificate.Subject.Contains("E=" + message.To))
        {
            encryptingCertificates.Add(certificate);
            //Encrypt the message
            message.SecureEncrypt(encryptingCertificates, EncryptingAlgorithm.TripleDes, false);
            return message;
        }
    }
    return null;
}
C#
Copy Code
private MailMessage getDecodedMessage(MailMessage message)
{
    //Load the decrypting certificate from an exported certificate file (may also be loaded from an X509Store)
    X509Certificate2 decryptingCertificate = new X509Certificate2(Application.StartupPath + "\\myCertificate.pfx");
    //Decode the message and import the signing certificate into the AddressBook certificate Store
    //(If the decrypting certificate is already present in the "MY" certificate store, 
    //the parameterless SecureDecode() may be used instead.)
    SignerInfoCollection signatories = message.SecureDecode(new X509Certificate2Collection(decryptingCertificate), true);
    //Optionally verify the signature and validate the certificate
    foreach (SignerInfo signator in signatories)
        signator.CheckSignature(false);
    return message;
}
Visual Basic
Copy Code
Private Function getSignedMessage(ByVal message As MailMessage) As MailMessage
    'Find the signing certificate in the "CurrentUser/My" certificate store
    'The following code results in the same signed message as "message.SecureSign();"
    Dim myPersonalStore As New X509Store(StoreName.My, StoreLocation.CurrentUser)
    myPersonalStore.Open(OpenFlags.ReadOnly)
    For Each certificate As X509Certificate2 In myPersonalStore.Certificates
        If certificate.Subject.Contains("E=" & message.From.ToString()) Then
            'Sign the message
            message.SecureSign(certificate, X509IncludeOption.ExcludeRoot, DigestAlgorithm.Sha1, True, False)
            Return message
        End If
    Next certificate
    Return Nothing
End Function
Visual Basic
Copy Code
Private Function getEncryptedMessage(ByVal message As MailMessage) As MailMessage
    'Find the encrypting certificate in the CurrentUser/AddressBook store
    'The following code results in the same encrypted message as "message.SecureEncrypt();"
    Dim encryptingCertificates As New X509Certificate2Collection()
    Dim addressBookStore As New X509Store(StoreName.AddressBook, StoreLocation.CurrentUser)
    addressBookStore.Open(OpenFlags.ReadOnly)
    For Each certificate As X509Certificate2 In addressBookStore.Certificates
        If certificate.Subject.Contains("E=" & message.To) Then
            encryptingCertificates.Add(certificate)
            'Encrypt the message
            message.SecureEncrypt(encryptingCertificates, EncryptingAlgorithm.TripleDes, False)
            Return message
        End If
    Next certificate
    Return Nothing
End Function
Visual Basic
Copy Code
Private Function getDecodedMessage(ByVal message As MailMessage) As MailMessage
    'Load the decrypting certificate from an exported certificate file (may also be loaded from an X509Store)
    Dim decryptingCertificate As New X509Certificate2(Application.StartupPath & "\myCertificate.pfx")
    'Decode the message and import the signing certificate into the AddressBook certificate Store
    '(If the decrypting certificate is already present in the "MY" certificate store, 
    'the parameterless SecureDecode() may be used instead.)
    Dim signatories As SignerInfoCollection = message.SecureDecode(New X509Certificate2Collection(decryptingCertificate), True)
    'Optionally verify the signature and validate the certificate
    For Each signator As SignerInfo In signatories
        signator.CheckSignature(False)
    Next signator
    Return message
End Function

PowerTCP Mail for .NET Documentation Version 4.3
© 2018 Dart Communications. All Rights Reserved.
Send comments on this topic